/**
 * @file src/datatypes/Heuristics.java
 */
package datatypes;

/** imports */
import java.io.FileInputStream;
import java.util.Scanner;
import java.util.Vector;

/**
 * Class to store heuristic information for a problem/graph
 */
public class Heuristics
{
    /** stored heuristic information */
    private Vector<Tuple<String, Float>> mHeuristicCosts;
    
    /**
     * Goal state for this heuristic is stored as heuristic with estimated distance 0.0f
     */

    /** Initialize state */
    boolean mInitialized;
    
    /** 
     * Default constructor 
     */
    public Heuristics()
    {
        mHeuristicCosts = null;
        mInitialized = false;
    }
    
    /**
     * Set goal for heuristic.
     * Creates a heuristic for the goal state, if necessary 
     * 
     * @param goal Goal state
     */
    public void setGoal(String goal)
    {
    	boolean exists = false;

    	for (Tuple<String, Float> t : mHeuristicCosts) {
        	if (t.first.compareTo(goal) == 0) {
        		exists = true;
        		/** set heuristic costs to zero */
        		t.second = 0.0f;
        		break;
        	}
    	}
    	/** if non existent, create heuristic distance for goal state */
    	if (!exists) {
    		Tuple<String, Float> t = new Tuple<String, Float>(goal, 0.0f);
    		mHeuristicCosts.addElement(t);
    	}
    } // setGoal(String goal)
    
    /**
     * Clear function
     */
    public void clear()
    {
        if (mInitialized) {
            mHeuristicCosts.clear();
        }
    }
    
    /**
     * Get heuristic costs for a state
     * 
     * @param name State to search for
     * 
     * @return Its heuristic costs (returns MAX_VALUE if state not found!)
     */
    public float get(String name)
    {
        if (mInitialized) {
            for (Tuple<String, Float> t: mHeuristicCosts) {
                if (t.first.compareTo(name) == 0) {
                    return t.second;
                }
            }
        }
        return Float.MAX_VALUE;
    }
    
    /**
     * Read in a heuristic by a given filename
     * 
     * @param fn Filename
     */
    public void read(String fn) throws Exception
    {
        /** Initialize graph/problem */
        if (mInitialized) {
            mHeuristicCosts.clear();
        } else {
            mHeuristicCosts = new Vector<Tuple<String, Float>>(0);
            mInitialized = true;
        }
        
        /** Find out system specific line separator */
        // final String NEWLINE = System.getProperty("line.separator");
        
        /** instantiate the actual scanner and input stream */
        Scanner scanner = new Scanner(new FileInputStream(fn));
        
        /** start reading file */
        try {
            
            /** ignore first line */
            scanner.nextLine();
            
            while (true) {
                
                /** first string, Node/State */
                String name = scanner.hasNext() ? scanner.next() : null;
                if (name == null) {
                	/** apparently reached end of file */
                	break;
                }
                
                /** second float, heuristic distance */
                float distance = scanner.hasNextFloat() ? scanner.nextFloat() : Float.MAX_VALUE;
                if (distance == Float.MAX_VALUE) { throw new Exception("distance not provided!"); }
                
                /** store data */
                
                /** check if name already exists */
                boolean exists = false;
                for (Tuple<String, Float> t : mHeuristicCosts) {
                	if (t.first.compareTo(name) == 0) {
                		exists = true;
                		t.second = distance; // update
                		System.out.println("Warning: Heuristic contains at least one state twice!");
                		break;
                	}
                }
                
                if (!exists) {
                	/** add new tuple */
                	Tuple<String, Float> t = new Tuple<String, Float>(name, distance);
                	mHeuristicCosts.addElement(t);
                }

                /** go to next line */
                if (scanner.hasNextLine()) {
                	scanner.nextLine();	
                } else {
                	break;
                }
                
            } // while(scanner.hasNextLine())
            
        } finally {
            /** close scanner when finished */
            scanner.close();
        }
        
        /** trim size */
        mHeuristicCosts.trimToSize();
               
    } // read(String fn)
    
} // class Heuristics
